#include <my_header.h>
#include<openssl/sha.h>
#include"client_transfile2.h"
// 定义命令类型
//typedef enum {
//    // 基本命令
//    CMD_CD = 0x01,          // 改变目录
//    CMD_LS = 0x02,          // 列出目录
//    CMD_PWD = 0x03,         // 当前工作目录
//
//    // 文件操作命令
//    CMD_PUTS = 0x10,        // 上传文件
//    CMD_GETS = 0x11,        // 下载文件
//    CMD_REMOVE = 0x12,      // 删除文件
//    CMD_MKDIR = 0x13,       // 创建目录
//    CMD_RMDIR = 0x14,       // 删除目录
//
//    // 大文件传输命令
//    CMD_FILE_CHECK=0x18,    //检测文件重复
//    CMD_FILE_EXISTS=0x19,   //文件重复
//    CMD_FILE_START = 0x20,  // 文件传输开始
//    CMD_FILE_DATA = 0x21,   // 文件数据块
//    CMD_FILE_END = 0x22,    // 文件传输结束
//
//    // 响应和错误
//    CMD_RESPONSE = 0x30,    // 正常响应
//    CMD_ERROR = 0xFF        // 错误响应
//} CommandType;
//
//// TLV协议头大小 (type + length + seq + checksum)
//#define TLV_HEADER_SIZE 13
//
//// TLV结构体
//typedef struct {
//    uint8_t type;           // 命令类型 (1字节)
//    uint32_t length;        // 数据长度 (4字节)
//    uint32_t seq;           // 可选。。。序列号 (4字节)
//    char value[1024];           // 数据长度（文件？）
//} TLV;
int send_all(int sockfd,const void *buf,size_t len){
    size_t total=0;
    const char*p=(const char*)buf;
    while(total<len){
        ssize_t n=send(sockfd,p+total,len-total,0);
        if(n<=0){
            if(n==0){
                printf("连接已关闭\n");
            }else{
                perror("发送数据失败");
            }
            return -1;
        }
        total+=n;
    }
    return 0;
}
//发送tlv
int sendTlv(int sockfd,TLV *tlv){
    char buf[TLV_HEADER_SIZE];
    buf[0]=tlv->type;
    memcpy(buf+1,&tlv->seq,4);
    memcpy(buf+5,&tlv->length,4);
    if(send_all(sockfd,buf,sizeof(buf))!=0){
        return -1;
    }
    if(tlv->length>0){
        return send_all(sockfd,tlv->value,tlv->length);
    }
    return 0;
}
int recv_all(int sockfd,void *buf,size_t len){
    size_t total=0;
    char *p=(char*)buf;
    while(total<len){
        ssize_t n=recv(sockfd,p+total,len-total,0);
        if(n<=0){
            if(n==0){
                printf("连接已关闭\n");
            }else{
                perror("接收数据失败");
            }
            return -1;
        }
        total+=n;
    }
    return 0;
}
//接收tlv
int recvTlv(int sockfd,TLV* tlv){
    char buf[TLV_HEADER_SIZE];
    recv_all(sockfd,buf,sizeof(buf));
    tlv->type=buf[0];
    memcpy(&tlv->seq,buf+1,4);
    memcpy(&tlv->length,buf+5,4);
    if(tlv->length>0){
        recv_all(sockfd,tlv->value,tlv->length);
    }
    return 0;
}
//计算文件hash值
int hash_count(const char *filename,char *hash_str){

    SHA256_CTX ctx;
    SHA256_Init(&ctx);
    char buf[4096];
    int fd=open(filename,O_RDWR);
    while(1){
        bzero(buf,sizeof(buf));
        ssize_t sret=read(fd,buf,sizeof(buf));
        SHA256_Update(&ctx,buf,sret);
        if(sret==0){
            break;
        }
    }
    unsigned char sha[32];
    SHA256_Final(sha,&ctx);
    for(int i=0;i<32;i++){
        char temp[3]={0};
        sprintf(temp,"%02x",sha[i]);
        strcat(hash_str,temp);
    }
    return 0;
}
//上传文件
int upload_file(int sockfd,const char *local_path){
    int fd=open(local_path,O_RDONLY);
    ERROR_CHECK(fd,-1,"open");
    struct stat st;
    fstat(fd,&st);
    printf("filesize=%ld\n",st.st_size);
    off_t file_size=st.st_size;
    uint32_t net_file_size = htonl(file_size); 
    off_t total_sent=0;
    off_t offset=0;
    TLV tlv_start;//将文件大小存在value中发给服务端
    memset(&tlv_start, 0, sizeof(TLV));
    tlv_start.type = CMD_FILE_START;
    tlv_start.length = sizeof(file_size);
    memcpy(tlv_start.value, &net_file_size, sizeof(net_file_size));
    sendTlv(sockfd,&tlv_start);
    while(total_sent<file_size){
        ssize_t sent=sendfile(sockfd,fd,&offset,file_size-total_sent);
        total_sent+=sent;
        printf("已上传:%.2f%%\r",(double)total_sent/file_size*100);
        fflush(stdout);
    }
    char hash_str[65]={0};
    hash_count(local_path,hash_str);
    TLV end_tlv;//将hash值存在value中发送给服务端
    memset(&end_tlv, 0, sizeof(TLV));
    end_tlv.type = CMD_FILE_END;
    end_tlv.length = 65;
    memcpy(end_tlv.value,&hash_str,end_tlv.length);
    sendTlv(sockfd, &end_tlv);
    printf("\n文件上传 %s: %ld/%ld 字节\n",
           (total_sent == file_size) ? "完成" : "失败",
           total_sent, file_size);
    close(fd);
    return 0;    
}
//下载文件
int download(int sockfd, const char *local_path){
    TLV file_header;
    recvTlv(sockfd,&file_header);
    if(file_header.type!=CMD_FILE_START){
        return -1;
    }
    uint32_t net_file_size;
    memcpy(&net_file_size,file_header.value,sizeof(net_file_size));
    int fd=open(local_path,O_WRONLY|O_CREAT|O_TRUNC,0666);
    ERROR_CHECK(fd,-1,"open");
    uint32_t file_size = ntohl(net_file_size); 
    printf("文件大小: %u 字节\n", file_size);
    uint64_t total_receive=0;
    char buff[4096];
    while(total_receive<file_size){
        size_t to_receive;
        size_t remaining=file_size-total_receive;
        if (remaining > sizeof(buff)) {
            to_receive = sizeof(buff);
        } else {
            to_receive = remaining;
        }
        ssize_t n=recv(sockfd,buff,to_receive,0);

        write(fd,buff,n);
        total_receive+=n;
        double progress = (double)total_receive / file_size * 100;
        static int last_reported = -1;
        if ((int)progress > last_reported) {
            printf("已下载: %.1f%%\r", progress);
            fflush(stdout);
            last_reported = (int)progress;
        }

    }
    TLV end_tlv;
    memset(&end_tlv, 0, sizeof(TLV));
    recvTlv(sockfd, &end_tlv);
    if(end_tlv.type==CMD_FILE_END){
        printf("文件下载结束\n");
    }
    printf("\n文件下载 %s: %ld/%d 字节\n",
           (total_receive == file_size) ? "完成" : "失败",
           total_receive, file_size);
    close(fd);

    return 0;
}
//发送hash值
int check_file_hash(int sockfd,const char *filename){
    char hash_str[65]={0};
    hash_count(filename,hash_str);
    TLV tlv;
    memset(&tlv,0,sizeof(TLV));
    tlv.type=CMD_FILE_CHECK;
    tlv.length=65;
    memcpy(tlv.value,&hash_str,tlv.length);
    printf("str=%s\n",tlv.value);
    sendTlv(sockfd,&tlv);
    TLV response;
    recvTlv(sockfd,&response);
    if(response.type==CMD_FILE_EXISTS){
        printf("已有重复文件\n");
        return -1;
    }else if (response.type==CMD_RESPONSE){
        printf("无重复\n");
    }
    return 0;
}
//int main(int argc, char *argv[])
//{
//    // ./01_client_tcp_chat 192.168.72.128 12345
//    ARGS_CHECK(argc,3);
//    int sockfd = socket(AF_INET,SOCK_STREAM,0);
//    struct sockaddr_in addr;
//    addr.sin_family = AF_INET;
//    addr.sin_port = htons(atoi(argv[2]));
//    addr.sin_addr.s_addr = inet_addr(argv[1]);
//    int ret = connect(sockfd,(struct sockaddr *)&addr,sizeof(addr));
//    ERROR_CHECK(ret,-1,"connect");
//    //    TLV tlv;
//    //    memset(&tlv, 0, sizeof(TLV));
//    //    tlv.type=0x01;
//    //    const char *message="cd";
//    //    tlv.length=strlen(message)+1;
//    //    strncpy(tlv.value, message, sizeof(tlv.value) - 1);
//    //    tlv.value[sizeof(tlv.value) - 1] = '\0';
//    //    sendTlv(sockfd,&tlv);
//    TLV puts_tlv;
//    memset(&puts_tlv, 0, sizeof(TLV));                                    
//    puts_tlv.type = CMD_PUTS;
//    puts_tlv.seq = 0;
//    puts_tlv.length = strlen("file1") + 1; 
//    strncpy(puts_tlv.value, "file1", puts_tlv.length);
//    if(check_file_hash(sockfd,"file1")==0){
//        sendTlv(sockfd,&puts_tlv);
//        upload_file(sockfd,"file1");
//    }
//    //    TLV gets_tlv;
//    //    memset(&gets_tlv, 0, sizeof(TLV));
//    //    gets_tlv.type = CMD_GETS;
//    //    gets_tlv.length = strlen("file2") + 1;
//    //    strncpy(gets_tlv.value, "file2", gets_tlv.length);
//    //    printf("%d\n",gets_tlv.type);
//    //    if(gets_tlv.type==CMD_GETS){
//    //        sendTlv(sockfd, &gets_tlv);
//    //        download(sockfd,"file2");
//    //    }
//    close(sockfd);
//    return 0;
//}
/* Usage:  */
